import 'dart:async';

import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

///  A widget that detects gestures.
/// * Supports Tap, DoubleTap, Move(start, update, end), Scale(start, update, end) and Long Press
/// * All callbacks be used simultaneously
///
/// For handle rotate event, please use rotateAngle on onScaleUpdate.
class OrayTouchListenerDetector extends StatefulWidget {
  /// Creates a widget that detects gestures.
  const OrayTouchListenerDetector(
      {super.key,
      required this.child,
      this.onSingleTapDown,
      this.onSingleTapUp,
      this.onMouseRightTap,
      this.onSingleQuickTap,
      this.onSingleMoveStart,
      this.onSingleMoveScrollUpdate,
      this.onSingleMoveEnd,
      this.onLongPress,
      this.onSingleLongMoveStart,
      this.onSingleLongMoveUpdate,
      this.onSingleLongMoveScrollUpdate,
      this.onSingleLongMoveEnd,
      this.onDoubleTap,
      this.onDoubleLongPress,
      this.onScaleStart,
      this.onScaleUpdate,
      this.onScaleEnd,
      this.onDoubleScrollStart,
      this.onDoubleScrollUpdate,
      this.onDoubleScrollEnd,
      this.onThreeTap,
      this.onThreeLongPress,
      this.onThreeScrollStart,
      this.onThreeScrollUpdate,
      this.onThreeScrollEnd,
      this.onPointerHover,
      this.onPointerSignal,
      this.behavior = HitTestBehavior.deferToChild,
      this.longPressTime = 200,
      this.longPressRange = 25,
      this.doubleScaleRange = 15,
      this.doubleScrollRange = 7});

  /// The widget below this widget in the tree.
  ///
  /// {@macro flutter.widgets.child}
  final Widget child;

  /// The pointer that previously triggered the onTapDown has also triggered onTapUp which ends up causing a tap.
  final TapEventListener? onSingleTapDown;

  final TapEventListener? onSingleTapUp;

  final TapEventListener? onMouseRightTap;

  final ValueChanged<Offset>? onSingleQuickTap;

  /// The pointer in the long press state and then made a move
  final VoidCallback? onSingleMoveStart;

  /// The pointer in the long press state and then made a move
  final Function(TouchListenerModel event, Offset offset)? onSingleMoveScrollUpdate;

  /// The pointer are no longer in contact with the screen after onLongPress event.
  final TapEventListener? onSingleMoveEnd;

  /// A pointer has remained in contact with the screen at the same location for a long period of time
  final VoidCallback? onLongPress;

  /// The pointer in the long press state and then made a move
  final TapEventListener? onSingleLongMoveStart;

  /// The pointer in the long press state and then made a move
  final TapEventListener? onSingleLongMoveUpdate;

  /// The pointer in the long press state and then made a move
  final Function(double distance, AxisDirection axisDirection)? onSingleLongMoveScrollUpdate;

  /// The pointer are no longer in contact with the screen after onLongPress event.
  final VoidCallback? onSingleLongMoveEnd;

  /// Tow pointers that previously triggered the onTapDown has also triggered onTapUp which ends up causing a tap.
  final VoidCallback? onDoubleTap;

  /// Tow pointers has remained in contact with the screen at the same location for a long period of time
  final VoidCallback? onDoubleLongPress;

  /// The pointers in contact with the screen have established a focal point and
  /// initial scale of 1.0.
  final VoidCallback? onScaleStart;

  /// The pointers in contact with the screen have indicated a new focal point
  /// and/or scale.
  final ValueChanged<double>? onScaleUpdate;

  /// The pointers are no longer in contact with the screen.
  final VoidCallback? onScaleEnd;

  /// Double finger scrolling begins
  final VoidCallback? onDoubleScrollStart;

  /// Double finger scrolling update
  final Function(Offset start, Offset move)? onDoubleScrollUpdate;

  /// Double finger scrolling end.
  final VoidCallback? onDoubleScrollEnd;

  /// Three finger on tap
  final VoidCallback? onThreeTap;

  /// Three finger on long press
  final VoidCallback? onThreeLongPress;

  /// Three finger scrolling begins
  final VoidCallback? onThreeScrollStart;

  /// Three finger scrolling update
  /// axisDirection is up down left right
  final Function(double distance, AxisDirection axisDirection)? onThreeScrollUpdate;

  /// Three finger scrolling end
  final VoidCallback? onThreeScrollEnd;

  /// How to behave during hit testing.
  final HitTestBehavior behavior;

  /// The maxium distance between the first touching position and the position when the long press event occurs.
  /// default: distanceSquared =  25
  final int longPressRange;

  /// A specific duration to detect long press
  final int longPressTime;

  ///The double touch distance
  final int doubleScaleRange;

  ///The double touch distance
  final int doubleScrollRange;

  final PointerHoverEventListener? onPointerHover;

  final PointerSignalEventListener? onPointerSignal;

  @override
  State<OrayTouchListenerDetector> createState() => _OrayTouchListenerDetectorState();
}

class _OrayTouchListenerDetectorState extends State<OrayTouchListenerDetector> {
  double initialScaleDistance = 1.0;
  Offset lastTouchUpPos = const Offset(0, 0);
  double tempScaling = 1;

  _TouchEventState touchEventState = _TouchEventState.unknown;
  final Map<int, TouchListenerModel> touches = {};

  get touchCount => touches.length;

  Offset? canQuickTapOffset;
  int canQuickTapTime = 0;

  void _debugLog(log) {
    print("_OrayTouchListenerDetectorState $log");
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
      behavior: widget.behavior,
      onPointerDown: onPointerDown,
      onPointerUp: onPointerUp,
      onPointerMove: onPointerMove,
      onPointerCancel: onPointerUp,
      onPointerSignal: widget.onPointerSignal,
      onPointerHover: widget.onPointerHover,
      child: widget.child,
    );
  }

  void onPointerDown(PointerDownEvent event) {
    ///屏蔽四指
    if (touchCount == 3) return;

    TouchListenerModel touch = TouchListenerModel(
        event.pointer, event.localPosition, event.buttons, DateTime.now().millisecondsSinceEpoch, _TouchState.down);
    touches[event.pointer] = touch;
    if (canQuickTapOffset != null) {
      double distance = (touch.startOffset - canQuickTapOffset!).distanceSquared;
      int nowTime = DateTime.now().millisecondsSinceEpoch;
      if (distance < 50 && nowTime - canQuickTapTime < 150) {
        touchEventState = _TouchEventState.onQuickTap;
        if (widget.onSingleQuickTap != null) {
          widget.onSingleQuickTap!(canQuickTapOffset!);
        }
        return;
      }
    }
    startLongPressTimer(event);
  }

  void onPointerMove(PointerMoveEvent event) {
    ///屏蔽四指
    if (!touches.containsKey(event.pointer)) return;

    TouchListenerModel? touch = touches[event.pointer];
    if (touch != null) {
      touch.currentOffset = event.localPosition;

      /// || touch.touchState == _TouchState.long
      /// 当按下状态开始移动超出长按的范围，就认为是移动
      if (touch.touchState == _TouchState.down &&
          !inLongPressRange(touch) &&
          touchEventState != _TouchEventState.onDoubleScale &&
          touchEventState != _TouchEventState.onDoubleScroll) {
        touch.touchState = _TouchState.move;
        touchEventState == _TouchEventState.unknown;
      } else if (touchCount == 1 && touchEventState == _TouchEventState.onSingleLongPress && !inLongPressRange(touch)) {
        _debugLog('onPointerMove 单指长按移动');
        touch.touchState = _TouchState.longAndMove;
        touchEventState = _TouchEventState.onSingleLongMove;

        if (widget.onSingleLongMoveStart != null) {
          widget.onSingleLongMoveStart!(touches.values.first);
        }
      } else if (touchCount == 2 &&
          touch.touchState != _TouchState.move &&
          touch.touchState != _TouchState.longAndMove &&
          !inLongPressRange(touch)) {
        _debugLog('onPointerMove 双指长按变移动');
        touch.touchState = _TouchState.move;
        checkMoveEventState();
      }
    }
    if (touchEventState == _TouchEventState.unknown) {
      checkMoveEventState();
    }

    if (touchEventState == _TouchEventState.onThreeLongPress) {
      if (widget.onThreeLongPress != null) {
        widget.onThreeLongPress!();
      }
    } else if (touchEventState == _TouchEventState.onThreeLongMove) {
      handlerThreeLongMove();
    } else if (touchEventState == _TouchEventState.onDoubleLongPress) {
      if (widget.onDoubleLongPress != null) {
        widget.onDoubleLongPress!();
      }
    } else if (touchEventState == _TouchEventState.onDoubleScale) {
      handlerDoubleScale();
    } else if (touchEventState == _TouchEventState.onDoubleScroll) {
      handlerDoubleScroll();
    } else if (touchEventState == _TouchEventState.onSingleLongPress) {
      if (widget.onLongPress != null) {
        widget.onLongPress!();
      }
    } else if (touchEventState == _TouchEventState.onSingleLongMove) {
      if (widget.onSingleLongMoveUpdate != null) {
        widget.onSingleLongMoveUpdate!(touches.values.first);
      }
      handlerSingleLongMove();
    } else if (touchEventState == _TouchEventState.onSingleMove) {
      handlerSingleMove();
    }
  }

  void onPointerUp(PointerEvent event) {
    ///屏蔽四指
    if (!touches.containsKey(event.pointer)) return;

    TouchListenerModel touch = touches[event.pointer]!;
    closeTouchTimer(touch);

    if (touchEventState == _TouchEventState.unknown) {
      checkTapEventState();

      if (touchEventState == _TouchEventState.onSingleTap) {
        if (widget.onSingleTapDown != null) {
          widget.onSingleTapDown!(touches.values.first);
        }
      } else if (touchEventState == _TouchEventState.onDoubleTap) {
        if (widget.onDoubleTap != null) {
          widget.onDoubleTap!();
        }
      } else if (touchEventState == _TouchEventState.onThreeTap) {
        if (widget.onThreeTap != null) {
          widget.onThreeTap!();
        }
      }
    } else if (touchEventState == _TouchEventState.onDoubleScale) {
      if (widget.onScaleEnd != null) {
        widget.onScaleEnd!();
      }
    } else if (touchEventState == _TouchEventState.onDoubleScroll) {
      if (widget.onDoubleScrollEnd != null) {
        widget.onDoubleScrollEnd!();
      }
    }

    if (touchCount == 1) {
      if (touchEventState == _TouchEventState.onSingleLongMove) {
        if (widget.onSingleLongMoveEnd != null) {
          widget.onSingleLongMoveEnd!();
        }
      } else if (touchEventState == _TouchEventState.onSingleMove) {
        if (widget.onSingleMoveEnd != null) {
          widget.onSingleMoveEnd!(touches.values.first);
        }
      } else if (touchEventState == _TouchEventState.onQuickTap) {
        canQuickTapOffset = null;
        canQuickTapTime = 0;
      } else if (touchEventState == _TouchEventState.onSingleTap) {
        canQuickTapOffset = touches.values.first.startOffset;
        canQuickTapTime = DateTime.now().millisecondsSinceEpoch;
      }

      if (touchEventState != _TouchEventState.onSingleLongPress &&
          touchEventState != _TouchEventState.onSingleMove &&
          touchEventState != _TouchEventState.onMouseRight &&
          touchEventState != _TouchEventState.unknown &&
          widget.onSingleTapUp != null) {
        widget.onSingleTapUp!(touches.values.first);
      }
    } else if (touchCount == 2) {
      if (touchEventState == _TouchEventState.onSingleLongPress ||
          touchEventState == _TouchEventState.onSingleLongMove ||
          touchEventState == _TouchEventState.onMouseRight) {
        _debugLog('onPointerDown 长按后右键');
        if (touchEventState == _TouchEventState.onSingleLongMove) {
          if (widget.onSingleTapUp != null) {
            widget.onSingleTapUp!(touches.values.first);
          }
        }
        touchEventState = _TouchEventState.onMouseRight;
        if (widget.onMouseRightTap != null) {
          widget.onMouseRightTap!(touch);
        }
      }
    }

    touches.removeWhere((key, value) => key == event.pointer);
    if (touches.isEmpty) {
      touchEventState = _TouchEventState.unknown;
    } else {
      if (touchEventState != _TouchEventState.onMouseRight) {
        for (var element in touches.values) {
          ///多指都需要清空一下触摸状态
          element.touchState = _TouchState.unknown;
        }
        touchEventState = _TouchEventState.unknown;
      }
    }
  }

  ///TODO  ******************   检查手势 start *********************
  void checkMoveEventState() {
    bool isLong = true;
    bool isMove = true;
    for (var element in touches.values) {
      if (element.touchState != _TouchState.move) {
        isMove = false;
      }
      if (element.touchState != _TouchState.long && element.touchState != _TouchState.longAndMove) {
        isLong = false;
      }
    }
    if (touchCount == 3) {
      if (isLong) {
        _debugLog('onPointerMove 三指长按');
        touchEventState = _TouchEventState.onThreeLongPress;
      } else if (isMove) {
        _debugLog('onPointerMove 三指移动');
        touchEventState = _TouchEventState.onThreeLongMove;
        if (widget.onThreeScrollStart != null) {
          widget.onThreeScrollStart!();
        }
      }
    } else if (touchCount == 2) {
      if (isLong) {
        _debugLog('onPointerMove 双指长按');
        touchEventState = _TouchEventState.onDoubleLongPress;
      } else if (isMove) {
        List<TouchListenerModel> positions = touches.values.toList();

        /// 计算距离变化
        double startDistance = _distance(positions[0].startOffset, positions[1].startOffset);
        double currentDistance = _distance(positions[0].currentOffset, positions[1].currentOffset);
        double distanceDelta = (currentDistance - startDistance).abs();

        /// 计算中心点变化
        Offset startMidPoint = _midPoint(positions[0].startOffset, positions[1].startOffset);
        Offset currentMidPoint = _midPoint(positions[0].currentOffset, positions[1].currentOffset);
        double midPointDelta = (currentMidPoint - startMidPoint).distance;
        _debugLog('onPointerMove 双指移动 distanceDelta：$distanceDelta midPointDelta：$midPointDelta');

        if (distanceDelta > widget.doubleScaleRange) {
          _debugLog('onPointerMove 双指缩放');
          touchEventState = _TouchEventState.onDoubleScale;
          if (widget.onScaleStart != null) {
            widget.onScaleStart!();
          }
        } else if (midPointDelta > widget.doubleScrollRange) {
          _debugLog('onPointerMove 双指滚动');
          touchEventState = _TouchEventState.onDoubleScroll;
          if (widget.onDoubleScrollStart != null) {
            widget.onDoubleScrollStart!();
          }
        }
      }
    } else if (touchCount == 1) {
      if (isLong) {
        _debugLog('onPointerMove 单指长按');
        touchEventState = _TouchEventState.onSingleLongPress;
      } else if (isMove) {
        _debugLog('onPointerMove 单指移动');
        touchEventState = _TouchEventState.onSingleMove;
        if (widget.onSingleMoveStart != null) {
          widget.onSingleMoveStart!();
        }
      }
    }
  }

  void checkTapEventState() {
    bool isDown = true;
    for (var element in touches.values) {
      if (element.touchState != _TouchState.down) {
        isDown = false;
      }
    }
    if (touchCount == 3) {
      if (isDown) {
        _debugLog('onPointerMove 三指点击');
        touchEventState = _TouchEventState.onThreeTap;
      }
    } else if (touchCount == 2) {
      if (isDown) {
        _debugLog('onPointerMove 双指点击');
        touchEventState = _TouchEventState.onDoubleTap;
      }
    } else if (touchCount == 1) {
      if (isDown) {
        _debugLog('onPointerMove 单指点击');
        touchEventState = _TouchEventState.onSingleTap;
      }
    }
  }

  ///TODO  ******************   检查手势 end *********************
  ///TODO  ******************   处理手势响应 start *********************

  ///相应单指滚动事件
  void handlerSingleMove() {
    Offset startMidPoint = getStartMidPoint();
    Offset currentMidPoint = getCurrentMidPoint();
    double threeScroll = _distance(startMidPoint, currentMidPoint);
    if (threeScroll > widget.longPressRange) {
      if (widget.onSingleMoveScrollUpdate != null) {
        widget.onSingleMoveScrollUpdate!(touches.values.first, currentMidPoint - startMidPoint);
      }
    }
  }

  ///相应单指长按滚动事件
  void handlerSingleLongMove() {
    Offset startMidPoint = getStartMidPoint();
    Offset currentMidPoint = getCurrentMidPoint();
    double threeScroll = _distance(startMidPoint, currentMidPoint);
    if (threeScroll > widget.longPressRange) {
      AxisDirection axisDirection = AxisDirection.up;
      double tempDx = currentMidPoint.dx - startMidPoint.dx;
      double tempDy = currentMidPoint.dy - startMidPoint.dy;

      /// 如果横向大于纵向
      if (tempDx.abs() > tempDy.abs()) {
        axisDirection = tempDx > 0 ? AxisDirection.right : AxisDirection.left;
      } else {
        axisDirection = tempDy > 0 ? AxisDirection.down : AxisDirection.up;
      }
      if (widget.onSingleLongMoveScrollUpdate != null) {
        widget.onSingleLongMoveScrollUpdate!(threeScroll, axisDirection);
      }
    }
  }

  ///相应三指滚动事件
  void handlerThreeLongMove() {
    Offset startMidPoint = getStartMidPoint();
    Offset currentMidPoint = getCurrentMidPoint();
    double threeScroll = _distance(startMidPoint, currentMidPoint);
    if (threeScroll > widget.longPressRange) {
      AxisDirection axisDirection = AxisDirection.up;
      double tempDx = currentMidPoint.dx - startMidPoint.dx;
      double tempDy = currentMidPoint.dy - startMidPoint.dy;

      /// 如果横向大于纵向
      if (tempDx.abs() > tempDy.abs()) {
        axisDirection = tempDx > 0 ? AxisDirection.right : AxisDirection.left;
      } else {
        axisDirection = tempDy > 0 ? AxisDirection.down : AxisDirection.up;
      }
      if (widget.onThreeScrollUpdate != null) {
        widget.onThreeScrollUpdate!(threeScroll, axisDirection);
      }
    }
  }

  ///相应双指放大缩小
  void handlerDoubleScale() {
    List<TouchListenerModel> positions = touches.values.toList();

    /// 计算距离变化
    double startDistance = _distance(positions[0].startOffset, positions[1].startOffset);
    double currentDistance = _distance(positions[0].currentOffset, positions[1].currentOffset);
    // 计算缩放因子
    double scaleFactor = currentDistance / startDistance;
    if (widget.onScaleUpdate != null) {
      widget.onScaleUpdate!(scaleFactor);
    }
  }

  void handlerDoubleScroll() {
    List<TouchListenerModel> positions = touches.values.toList();

    /// 计算中心点变化
    Offset startMidPoint = _midPoint(positions[0].startOffset, positions[1].startOffset);
    Offset currentMidPoint = _midPoint(positions[0].currentOffset, positions[1].currentOffset);
    double midPointDelta = (currentMidPoint - startMidPoint).distance;

    if (widget.onDoubleScrollUpdate != null) {
      widget.onDoubleScrollUpdate!(startMidPoint, currentMidPoint - startMidPoint);
    }
  }

  ///TODO  ******************   处理手势响应 end *********************

  /// 计算手势开始的中心点
  Offset getStartMidPoint() {
    double tempDx = 0;
    double tempDy = 0;
    for (var element in touches.values) {
      tempDx += element.startOffset.dx;
      tempDy += element.startOffset.dy;
    }
    return Offset(tempDx / touchCount, tempDy / touchCount);
  }

  /// 计算手势当前的中心点
  Offset getCurrentMidPoint() {
    double tempDx = 0;
    double tempDy = 0;
    for (var element in touches.values) {
      tempDx += element.currentOffset.dx;
      tempDy += element.currentOffset.dy;
    }
    return Offset(tempDx / touchCount, tempDy / touchCount);
  }

  // 计算两点之间的距离
  double _distance(Offset p1, Offset p2) {
    return (p1 - p2).distance;
  }

  // 计算两点的中点
  Offset _midPoint(Offset p1, Offset p2) {
    return Offset((p1.dx + p2.dx) / 2, (p1.dy + p2.dy) / 2);
  }

  ///等待时间后根据移动距离判断状态
  void startLongPressTimer(PointerDownEvent event) {
    TouchListenerModel? touchTemp = touches[event.pointer];
    if (touchTemp != null) {
      closeTouchTimer(touchTemp);
      touchTemp.longPressTimer = Timer(Duration(milliseconds: widget.longPressTime), () {
        TouchListenerModel? touch = touches[event.pointer];
        if (touch != null) {
          ///如果是按下状态
          if (touch.touchState == _TouchState.down) {
            ///如果在长按的范围内
            if (inLongPressRange(touch)) {
              touch.touchState = _TouchState.long;
            } else {
              touch.touchState = _TouchState.move;
            }
          }
          closeTouchTimer(touch);
        }
      });
    }
  }

  bool inLongPressRange(TouchListenerModel touch) {
    return (touch.currentOffset - touch.startOffset).distanceSquared < widget.longPressRange;
  }

  void closeTouchTimer(TouchListenerModel touch) {
    if (touch.longPressTimer != null) {
      touch.longPressTimer!.cancel();
      touch.longPressTimer = null;
    }
  }
}

///整体手势的触摸事件状态
enum _TouchEventState {
  onSingleTap,
  onQuickTap,
  onSingleMove,
  onSingleLongPress,
  onSingleLongMove,
  onMouseRight,
  onDoubleTap,
  onDoubleLongPress,
  onDoubleScale,
  onDoubleScroll,
  onThreeTap,
  onThreeLongPress,
  onThreeLongMove,
  unknown,
}

///单次点击的触摸状态
enum _TouchState { down, long, move, longAndMove, unknown }

class TouchListenerModel {
  ///按钮id
  int pointer;

  ///按钮类型
  int buttons;

  ///按钮点击落下时间
  int downTime;

  ///按钮开始位置
  Offset startOffset;

  ///按钮当前位置
  late Offset currentOffset;

  /// 按钮当前状态
  _TouchState touchState = _TouchState.unknown;

  Timer? longPressTimer;

  TouchListenerModel(this.pointer, this.startOffset, this.buttons, this.downTime, this.touchState) {
    this.currentOffset = startOffset;
  }
}

/// Signature for listening to [TapEvent] events.
///
/// Used by [XGestureDetector].
typedef TapEventListener = void Function(TouchListenerModel event);
